@rendermode InteractiveServer
@using AliasVault.Shared.Server.Models
@using AliasVault.Shared.Server.Services
@inject ServerSettingsService SettingsService

<div class="md:col-span-2 p-4 bg-white border border-gray-200 rounded-lg shadow-sm dark:border-gray-700 sm:p-6 dark:bg-gray-800 max-h-[500px]">
    @if (IsLoading)
    {
        <LoadingIndicator />
    }
    else
    {
        <ApexChart TItem="DailyUserCount"
            Title="@($"User activity - last {DaysToShow} days")"
            Height="400">
            <ApexPointSeries TItem="DailyUserCount"
                        Items="TotalDailyUserCounts"
                        SeriesType="@SeriesType.Area"
                        Name="Total Active Users"
                        XValue="@(e => e.Date.ToString("MM-dd"))"
                        YValue="@(e => e.Count)" />
            <ApexPointSeries TItem="DailyUserCount"
                        Items="DailyUserCounts"
                        SeriesType="@SeriesType.Area"
                        Name="Returning Users"
                        XValue="@(e => e.Date.ToString("MM-dd"))"
                        YValue="@(e => e.Count)" />

            <ApexPointSeries TItem="DailyUserCount"
                        Items="NewUserRegistrations"
                        SeriesType="@SeriesType.Area"
                        Name="New Registrations"
                        XValue="@(e => e.Date.ToString("MM-dd"))"
                        YValue="@(e => e.Count)" />
        </ApexChart>
    }
</div>


@code {
    private bool IsLoading { get; set; } = true;
    private List<DailyUserCount> DailyUserCounts = new();
    private List<DailyUserCount> TotalDailyUserCounts = new();
    private List<DailyUserCount> NewUserRegistrations = new();
    private int DaysToShow { get; set; } = 30;
    private ServerSettingsModel Settings { get; set; } = new();

    /// <inheritdoc/>
    protected override async Task OnInitializedAsync()
    {
        Settings = await SettingsService.GetAllSettingsAsync();

        // Set the number of days to show to the auth log retention days up to a maximum of 60 days (for performance reasons).
        int maxDays = Math.Min(Settings.AuthLogRetentionDays, 60);

        // If the auth log retention days is 0 (unlimited), set the number of days to show to 60.
        DaysToShow = maxDays == 0 ? 60 : maxDays;
    }

    /// <summary>
    /// Refreshes the data displayed on the card.
    /// </summary>
    public async Task RefreshData()
    {
        IsLoading = true;
        StateHasChanged();

        // Get daily active user counts for the past 14 days
        await GetDailyActiveUserCounts();

        IsLoading = false;
        StateHasChanged();
    }

    /// <summary>
    /// Get daily active user counts for up to the last 90 days to display on the chart.
    /// </summary>
    private async Task GetDailyActiveUserCounts()
    {
        DailyUserCounts = new List<DailyUserCount>();
        TotalDailyUserCounts = new List<DailyUserCount>();
        NewUserRegistrations = new List<DailyUserCount>();
        await using var dbContext = await DbContextFactory.CreateDbContextAsync();

        // Define the date range (defaults to amount of days in auth log retention, with a maximum of 90 days)
        var endDate = DateTime.UtcNow.Date;
        var startDate = endDate.AddDays(-DaysToShow);

        // Get total active users (all users who logged in based on auth logs)
        var totalUsersByDay = await dbContext.AuthLogs
            .Where(l => l.Timestamp >= startDate && l.Timestamp < endDate && l.IsSuccess && l.Username != "admin")
            .GroupBy(x => x.Timestamp.Date)
            .Select(g => new { Day = g.Key, Count = g.Select(x => x.Username).Distinct().Count() })
            .ToListAsync();

        // Get new user registrations by day
        var newUsersByDay = await dbContext.AliasVaultUsers
            .Where(u => u.CreatedAt >= startDate && u.CreatedAt < endDate && u.UserName != "admin")
            .GroupBy(u => u.CreatedAt.Date)
            .Select(g => new { Day = g.Key, Count = g.Count() })
            .ToListAsync();

        // Fill in the results for all days
        for (int i = 0; i < DaysToShow; i++)
        {
            // Subtract 1 day to avoid showing the current day as those numbers are not complete yet.
            var day = endDate.AddDays(-i - 1);

            var totalActiveCount = totalUsersByDay.FirstOrDefault(d => d.Day == day)?.Count ?? 0;
            var registeredUsersCount = newUsersByDay.FirstOrDefault(d => d.Day == day)?.Count ?? 0;

            // Calculate the number of returning users by subtracting the number of users registered that day from the total active users.
            var returningUsersCount = totalActiveCount - registeredUsersCount;

            DailyUserCounts.Add(new DailyUserCount
            {
                Date = day,
                Count = returningUsersCount
            });

            TotalDailyUserCounts.Add(new DailyUserCount
            {
                Date = day,
                Count = totalActiveCount
            });

            NewUserRegistrations.Add(new DailyUserCount
            {
                Date = day,
                Count = registeredUsersCount
            });
        }
    }

    private sealed class DailyUserCount
    {
        public DateTime Date { get; set; }
        public int Count { get; set; }
    }
}
